package com.ruitu.health_1.view;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;

import com.ruitu.health_1.R;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class TapVerificationView extends View {

    /*画布宽高*/
    private int width;
    private int height;

    private Bitmap oldBitmap;
    /*根据准备的图片重新调整尺寸后的背景图*/
    private Bitmap bgBitmap;
    private Paint bgPaint;
    private RectF bgRectF;

    /*验证码文字画笔*/
    private Paint textPaint;
    private Paint selectPaint;
    private Paint selectTextPaint;
    private List<Region> regions = new ArrayList<Region>();

    private Random random;

    private String fonts = "";
    private int checkCode = 0;


    private List<Point> tapPoints = new ArrayList<Point>();
    private List<Integer> tapIndex = new ArrayList<Integer>();

    private List<Point> textPoints = new ArrayList<Point>();
    private List<Integer> degrees = new ArrayList<Integer>();

    private boolean isInit = true;

    public TapVerificationView(Context context) {
        this(context, null);
    }

    public TapVerificationView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public TapVerificationView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        oldBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);

        bgPaint = new Paint();
        bgPaint.setAntiAlias(true);
        bgPaint.setFilterBitmap(true);

        textPaint = new Paint();
        textPaint.setAntiAlias(true);
        textPaint.setFakeBoldText(true);
        textPaint.setColor(Color.parseColor("#AA000000"));
        textPaint.setShadowLayer(3, 2, 2, Color.RED);
        textPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN));

        selectPaint = new Paint();
        selectPaint.setAntiAlias(true);
        selectPaint.setStyle(Paint.Style.FILL);
        selectPaint.setColor(Color.WHITE);


        selectTextPaint = new Paint();

        random = new Random();

        int temp = fonts.length() - 1;
        while (temp > -1) {
            checkCode += temp * Math.pow(10, temp);
            temp--;
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int minimumWidth = getSuggestedMinimumWidth();
        int minimumHeight = getSuggestedMinimumHeight();
        width = measureSize(minimumWidth, widthMeasureSpec);
        height = width;
        bgBitmap = clipBitmap(oldBitmap, width, height);
        bgRectF = new RectF(0, 0, width, height);
        textPaint.setTextSize(width / 6);
        setMeasuredDimension(width, height);
    }

    public Bitmap clipBitmap(Bitmap bm, int newWidth, int newHeight) {
        int width = bm.getWidth();
        int height = bm.getHeight();
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;
        Matrix matrix = new Matrix();
        matrix.postScale(scaleWidth, scaleHeight);
        return Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
    }

    private int measureSize(int defaultSize, int measureSpec) {
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        int result = defaultSize;
        switch (mode) {
            case MeasureSpec.UNSPECIFIED:
                result = defaultSize;
                break;
            case MeasureSpec.AT_MOST:
            case MeasureSpec.EXACTLY:
                result = size;
                break;
        }
        return result;
    }

    public static int dp2px(float dp) {
        float density = Resources.getSystem().getDisplayMetrics().density;
        return (int) (density * dp + 0.5f);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                int x = (int) event.getX();
                int y = (int) event.getY();
                for (Region region : regions) {
                    if (region.contains(x, y)) {

                        isInit = false;

                        int index = regions.indexOf(region);
                        if (!tapIndex.contains(index)) {
                            tapIndex.add(index);
                            tapPoints.add(new Point(x, y));
                        }


                        if (tapIndex.size() == fonts.length()) {
                            StringBuilder s = new StringBuilder();
                            for (Integer i : tapIndex) {
                                s.append(i);
                            }
                            int result = Integer.parseInt(s.toString());
                            if (result == checkCode) {
                                handler.sendEmptyMessage(1);
                            } else {
                                handler.sendEmptyMessage(0);
                            }
                        }

                        invalidate();
                    }
                }
        }
        return false;
    }

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            int result = msg.what;
            switch (result) {
                case 1:
                    Toast.makeText(getContext(), "验证成功!", Toast.LENGTH_SHORT).show();
                    listener.onResult(true);
                    break;
                default:
                    Toast.makeText(getContext(), "验证失败!", Toast.LENGTH_SHORT).show();
                    postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            reDrew();
                            listener.onResult(false);
                        }
                    }, 1000);
                    break;
            }
        }
    };

    private boolean checkCover(int x, int y) {
        for (Region region : regions) {
            if (region.contains(x, y)) {
                return true;
            }
        }
        return false;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        regions.clear();
        canvas.drawBitmap(bgBitmap, null, bgRectF, bgPaint);

        /*在处理点击的时候需要绘制用户点击的顺序，这时候要判断是初始化验证码，还是用户在点击需要绘制点击的序号
         * 如果是初始化验证码，就随机生成文字，绘制文字
         * 如果是用户在点击，需要绘制点击的顺序，这时候就不能重新随机生成坐标点，要让文字位置保持不动才行，否则会出现点击一次，随机生成一次验证码的情况
         * */
        if (isInit) {

            textPoints.clear();
            degrees.clear();
            tapIndex.clear();
            tapPoints.clear();

            for (int i = 0; i < fonts.length(); i++) {
                /*这里把文字倒着写是为了后面的验证方便*/
                String s = String.valueOf(fonts.charAt(fonts.length() - i - 1));
                int textSize = (int) textPaint.measureText(s);
                canvas.save();
                /*在指定范围随机生成坐标点*/
                int x = random.nextInt(width - textSize);
                int y = random.nextInt(height - textSize);

                /*如果检测到点和文字区域有重合，则要重新随机生成点坐标，这里四个条件，分别是如果以(x,y)为文字绘制坐标  的  四个角的位置
                 * 这里有一点绕，理解困难的最好在草纸上比划比划*/
                while (checkCover(x, y) || checkCover(x, y + textSize) || checkCover(x + textSize, y) || checkCover(x + textSize, y + textSize)) {
                    x = random.nextInt(width - textSize);
                    y = random.nextInt(height - textSize);
                }

                textPoints.add(new Point(x, y));
                canvas.translate(x, y);
                /*随机生成一个30以内的整数，使文字倾斜一定的角度*/
                int degree = random.nextInt(30);
                degrees.add(degree);
                canvas.rotate(degree);
                canvas.drawText(s, 0, textSize, textPaint);
                regions.add(new Region(x, y, textSize + x, textSize + y));
                canvas.restore();
            }
        } else {
            for (int i = 0; i < fonts.length(); i++) {
                String s = String.valueOf(fonts.charAt(fonts.length() - i - 1));
                int textSize = (int) textPaint.measureText(s);

                canvas.save();

                /*效果图上用户点击文字会出现序号显示这是点击的第几个，而验证码文字没有变化，其实验证码文字这里也重新绘制了，
                只不过还是原来的位置、角度*/
                int x = textPoints.get(i).x;
                int y = textPoints.get(i).y;
                int degree = degrees.get(i);
                canvas.translate(x, y);
                canvas.rotate(degree);
                canvas.drawText(s, 0, textSize, textPaint);
                regions.add(new Region(x, y, textSize + x, textSize + y));
                canvas.restore();
            }

            /*绘制点击的序号*/
            for (Point point : tapPoints) {
                int index = tapPoints.indexOf(point) + 1;
                String s = index + "";
                int textSize = width / 6 / 3;
                selectTextPaint.setTextSize(textSize);
                canvas.drawCircle(point.x, point.y, textSize, selectPaint);


                Rect rect = new Rect();
                selectTextPaint.getTextBounds(s, 0, 1, rect);

                int textWidth = rect.width();
                int textHeight = rect.height();

                canvas.drawText(s, point.x - textWidth / 2, point.y + textHeight / 2, selectTextPaint);
            }
        }
    }

    public void reDrew() {
        textPoints.clear();
        degrees.clear();
        tapIndex.clear();
        tapPoints.clear();

        isInit = true;

        invalidate();
    }

    public void setVerifyText(String s){
        fonts = s;

        checkCode = 0;
        int temp = fonts.length() - 1;
        while (temp > -1) {
            checkCode += temp * Math.pow(10, temp);
            temp--;
        }

        invalidate();
    }

    private OnVerifyListener listener;

    public void setVerifyListener(OnVerifyListener listener) {
        this.listener = listener;
    }

}